<template>
	<view class="e-scroll-view" :class="bemClass" :style="[finalPagingStyle]">
		<!-- #ifndef APP-PLUS -->
		<view v-if="cssSafeAreaInsetBottom === -1" class="e-scroll-view__safe-area-inset-bottom"></view>
		<!-- #endif -->
		<!-- 二楼view -->
		<view
			v-if="showF2 && param.showRefresherF2"
			@touchmove.stop.prevent
			class="e-scroll-view__f2"
			:style="[{ transform: f2Transform, transition: `transform .2s linear`, height: superContentHeight + 'px', 'z-index': param.f2ZIndex }]"
		>
			<slot name="f2" />
		</view>
		<view class="e-scroll-view__super" :class="[is('scroll', !param.usePageScroll)]" :style="[finalScrollViewStyle]">
			<view class="e-scroll-view__super-container" :class="[is('absoulte', finalIsOldWebView)]">
				<scroll-view
					class="e-scroll-view__super-container__scroll-view"
					ref="scrollViewElRef"
					:class="[is('absoulte', !param.usePageScroll), is('hide-scrollbar', !param.showScrollbar)]"
					:style="[chatRecordRotateStyle]"
					:scroll-top="scrollTop"
					:scroll-x="param.scrollX"
					:scroll-y="finalScrollable"
					:enable-back-to-top="finalEnableBackToTop"
					:show-scrollbar="param.showScrollbar"
					:scroll-with-animation="finalScrollWithAnimation"
					:scroll-into-view="param.scrollIntoView"
					:lower-threshold="finalLowerThreshold"
					:upper-threshold="5"
					:refresher-enabled="finalRefresherEnabled && !param.useCustomRefresher"
					:refresher-threshold="finalRefresherThreshold"
					:refresher-default-style="finalRefresherDefaultStyle"
					:refresher-background="param.refresherBackground"
					:refresher-triggered="finalRefresherTriggered"
					@scroll="handleScroll"
					@scrolltolower="_onScrollToLower"
					@scrolltoupper="_onScrollToUpper"
					@refresherrestore="_onRestore"
					@refresherrefresh="handleRefresh(true)"
				>
					<view class="e-scroll-view__touch-view" 
					<!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
						@touchstart="refresherTouchstart" @touchmove="_refresherTouchmove" @touchend="_refresherTouchend" @touchcancel="_refresherTouchend"
						<!-- #endif -->
						<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
						@touchstart="scrollWxs.touchstart" @touchmove="scrollWxs.touchmove" @touchend="scrollWxs.touchend" @touchcancel="scrollWxs.touchend"
						@mousedown="scrollWxs.mousedown" @mousemove="scrollWxs.mousemove" @mouseup="scrollWxs.mouseup" @mouseleave="scrollWxs.mouseleave"
						<!-- #endif -->
						>

						<view
							v-if="finalRefresherFixedBacHeight > 0"
							class="e-scroll-view__touch-view--fixed-bac-view"
							:style="[{ background: param.refresherFixedBackground, height: `${finalRefresherFixedBacHeight}px` }]"
						></view>
						<view
							class="e-scroll-view__main"
							:style="[scrollViewInStyle, { transform: finalRefresherTransform, transition: refresherTransition }]"
							<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
							:change:prop="scrollWxs.propObserver" :prop="wxsPropType" :data-refresherThreshold="finalRefresherThreshold"
							:data-refresherF2Enabled="param.refresherF2Enabled" :data-refresherF2Threshold="finalRefresherF2Threshold" :data-isIos="isIos"
							:data-loading="isRefresherInComplete" :data-useChatRecordMode="param.useChatRecordMode" :data-refresherEnabled="param.refresherEnabled"
							:data-useCustomRefresher="param.useCustomRefresher" :data-pageScrollTop="wxsPageScrollTop" :data-scrollTop="wxsScrollTop"
							:data-refresherMaxAngle="param.refresherMaxAngle" :data-refresherNoTransform="param.refresherNoTransform"
							:data-refresherAecc="param.refresherAngleEnableChangeContinued" :data-usePageScroll="param.usePageScroll"
							:data-watchTouchDirectionChange="param.watchTouchDirectionChange" :data-oldIsTouchmoving="isTouchmoving" :data-refresherOutRate="finalRefresherOutRate"
							:data-refresherPullRate="finalRefresherPullRate" :data-watchTouchmove="param.watchTouchmove" :data-hasTouchmove="param.watchRefresherTouchmove"
							<!-- #endif -->
							<!-- #ifdef APP-VUE || H5 -->
							:change:renderPropIsIosAndH5="render.renderPropIsIosAndH5Change" :renderPropIsIosAndH5="isIosAndH5"
							<!-- #endif -->
							>
							<view
								v-if="showRefresher"
								class="e-scroll-view__refresher-view"
								:style="[
									{
										'margin-top': `-${finalRefresherThreshold + refresherThresholdUpdateTag}px`,
										background: param.refresherBackground,
										opacity: isTouchmoving ? 1 : 0
									}
								]"
							>
								<view
									class="e-scroll-view__custom-refresher-container"
									:style="[{ height: `${finalRefresherThreshold}px`, background: param.refresherBackground }]"
								>
									<view
										v-if="param.useRefresherStatusBarPlaceholder"
										class="e-scroll-view__custom-refresher-status-bar-placeholder"
										:style="[{ height: `${systemInfo.statusBarHeight}px` }]"
									/>
									<!-- 下拉刷新view -->
									<view class="e-scroll-view__custom-refresher-slot-view">
										<slot
											v-if="!($slots.refresherComplete && refresherStatus === R.Complete) && !($slots.refresherF2 && refresherStatus === R.GoF2)"
											:refresherStatus="refresherStatus"
											name="refresher"
										/>
									</view>
									<slot v-if="$slots.refresherComplete && refresherStatus === R.Complete" name="refresherComplete" />
									<slot v-else-if="$slots.refresherF2 && refresherStatus === R.GoF2" name="refresherF2" />
									<e-refresh
										ref="refresh"
										v-else-if="!showCustomRefresher"
										class="e-scroll-view__custom-refresher-refresh"
										:style="[{ height: `${finalRefresherThreshold - finalRefresherThresholdPlaceholder}px` }]"
										:status="refresherStatus"
										:defaultThemeColor="finalRefresherThemeStyle"
										:defaultText="param.refresherDefaultText"
										:pullingText="param.refresherPullingText"
										:refreshingText="param.refresherRefreshingText"
										:completeText="param.refresherCompleteText"
										goF2Text="finalRefresherGoF2Text"
										:defaultImg="param.refresherDefaultImg"
										:pullingImg="param.refresherPullingImg"
										:refreshingImg="param.refresherRefreshingImg"
										:completeImg="param.refresherCompleteImg"
										:refreshingAnimated="param.refresherRefreshingAnimated"
										:refresherTimeText="param.refresherTimeText"
										:imgStyle="param.refresherImgStyle"
										:titleStyle="param.refresherTitleStyle"
										:updateTimeStyle="param.refresherUpdateTimeStyle"
										:unit="param.unit"
									/>
								</view>
							</view>
							<view class="e-scroll-view__container" :style="[{ justifyContent: param.useChatRecordMode ? 'flex-end' : 'flex-start' }]">
								<!-- 主体内容 -->
								<view class="e-scroll-view__container-content" :style="[finalPagingContentStyle]">
									<slot />
									<!-- 虚拟列表底部占位view -->
									<view v-if="param.useVirtualList" :style="[{ height: virtualPlaceholderBottomHeight + 'px' }]" />
									<view
										v-if="param.safeAreaInsetBottom && param.useSafeAreaPlaceholder && !param.useChatRecordMode"
										:style="[{ height: addUnit(safeAreaBottom) }]"
									/>
								</view>
							</view>
						</view>
					</view>
				</scroll-view>
			</view>
		</view>
	</view>
</template>

<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
<script src="./scrollWxs.wxs" module="scrollWxs" lang="wxs"></script>
<!-- #endif -->
<script src="./render.js" module="render" lang="renderjs"></script>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
	methods: {
		// uniapp wxs的callMethod不支持setup写法，改为自定义callMethod
		callMethod(params: any[]) {
			this._.exposed[params[0]]?.(...params.slice(1));
		}
	}
});
</script>
<script setup lang="ts">
import { computed } from 'vue';
import { useHooks } from './hooks';
import { addUnit } from '../../libs/utils/_utils';
import { useNamespace } from '../../libs/hooks';
import { Enum } from './env';

type Props = {
	param: {
		useChatRecordMode?: boolean;
		chatRecordMoreOffset: string | number;
		autoHideKeyboardWhenChat?: boolean;
		autoAdjustPositionWhenChat?: boolean;
		chatAdjustPositionOffset: string | number;
		autoToBottomWhenChat?: boolean;
		showChatLoadingWhenReload?: boolean;
		chatLoadingMoreDefaultAsLoading?: boolean;
		watchTouchmove?: boolean;
		refresherThemeColor?: string;
		refresherImgStyle?: Record<string, any>;
		refresherTitleStyle?: Record<string, any>;
		refresherUpdateTimeStyle?: Record<string, any>;
		watchRefresherTouchmove?: boolean;
		refresherDefaultDuration: number | string;
		refresherCompleteDelay?: number | string;
		refresherCompleteDuration: number | string;
		refresherRefreshingScrollable?: boolean;
		refresherCompleteScrollable?: boolean;
		useCustomRefresher?: boolean;
		refresherFps: number | string;
		refresherMaxAngle?: number | string;
		refresherAngleEnableChangeContinued?: boolean;
		refresherDefaultText?: string;
		refresherPullingText?: string;
		refresherRefreshingText?: string;
		refresherCompleteText?: string;
		refresherGoF2Text?: string;
		refresherDefaultImg?: string;
		refresherPullingImg?: string;
		refresherRefreshingImg?: string;
		refresherCompleteImg?: string;
		refresherRefreshingAnimated?: boolean;
		refresherEndBounceEnabled?: boolean;
		refresherEnabled?: boolean;
		refresherThreshold?: number | string;
		refresherDefaultColor?: string;
		refresherBackground?: string;
		refresherFixedBackground?: string;
		refresherFixedBacHeight: number | string;
		refresherOutRate: number;
		refresherF2Enabled?: boolean;
		refresherF2Threshold?: number | string;
		refresherF2Duration: number | string;
		showRefresherF2?: boolean;
		refresherPullRate: number;
		refresherTimeText?: string;
		refresherVibrate?: boolean;
		refresherNoTransform?: boolean;
		useRefresherStatusBarPlaceholder?: boolean;
		nvueListIs: 'list' | 'waterfall' | 'scroller';
		nvueWaterfallConfig?: Record<string, any>;
		nvueBounce?: boolean;
		nvueFastScroll?: boolean;
		nvueListId?: string;
		nvueRefresherStyle?: Record<string, any>;
		nvuePagingEnabled?: boolean;
		hideNvueBottomTag?: boolean;
		offsetAccuracy?: number;
		lowerThreshold: number | string;
		usePageScroll?: boolean;
		scrollable?: boolean;
		showScrollbar?: boolean;
		scrollX?: boolean;
		scrollToTopBounceEnabled?: boolean;
		scrollToBottomBounceEnabled?: boolean;
		scrollWithAnimation?: boolean;
		scrollIntoView?: string;
		backToTopWithAnimate?: boolean;
		delay?: number | string;
		minDelay?: number | string;
		pagingStyle?: Record<string, any>;
		height?: string;
		width?: string;
		maxWidth?: string;
		bgColor?: string;
		pagingContentStyle?: Record<string, any>;
		autoHeight?: boolean;
		autoHeightAddition: number | string;
		defaultThemeColor?: string;
		fixed?: boolean;
		safeAreaInsetBottom?: boolean;
		useSafeAreaPlaceholder?: boolean;
		bottomBgColor?: string;
		topZIndex?: number;
		superContentZIndex?: number;
		contentZIndex?: number;
		f2ZIndex?: number;
		autoFullHeight?: boolean;
		watchTouchDirectionChange?: boolean;
		unit?: string;
		enableBackToTop?: boolean;
		useVirtualList?: boolean;
		cellHeightMode?: string;
		preloadPage?: number | string;
		fixedCellHeight?: number | string;
		virtualListCol?: number | string;
		virtualScrollFps?: number | string;
		forceCloseInnerList?: boolean;
		virtualListData?: Array<any>;
		virtualListPageSize?: number | string;
	};
};

const props = withDefaults(defineProps<Props>(), {});
const emit = defineEmits([
	'refresh',
	'refresherF2Change',
	'update:scrollTop',
	'scroll',
	'scrolltolower',
	'scrolltoupper',
	'scrollTopChange',
	'refresherStatusChange',
	'update:refresherStatus',
	'restore',
	'hidedKeyboard',
	'refresherTouchstart',
	'refresherTouchmove',
	'refresherTouchend',
	'touchstart',
	'touchmove',
	'touchend',
	'update:cellStyle',
	'cellStyleChange',
	'update:virtualList',
	'virtualListChange'
]);

const { is } = useNamespace('scroll-list');

const option = computed(() => props.param);

const R = Enum.Refresher;

const {
	chatRecordRotateStyle,
	isChatRecordModeHasTransform,
	isChatRecordModeAndNotInversion,
	isChatRecordModeAndInversion,
	cssSafeAreaInsetBottom,
	getCssSafeAreaInsetBottom,
	safeAreaBottom,
	refresherStatus,
	systemInfo,
	isIos,
	wxsScrollTop,
	scrollEnable,
	superContentHeight,
	scrollTop,
	oldScrollTop,
	scrollViewStyle,
	scrollViewInStyle,
	scrollViewElRef,
	finalScrollViewStyle,
	setAutoHeight,
	pageScrollTop,
	privateScrollWithAnimation,
	finalScrollWithAnimation,
	finalScrollTop,
	finalScrollable,
	scrollToTop,
	scrollToBottom,
	scrollIntoViewById,
	scrollIntoViewByNodeTop,
	scrollToY,
	scrollIntoViewByIndex,
	scrollIntoViewByView,
	updatePageScrollTop,
	updateScrollViewScrollTop,
	listTopAagElRef,
	refreshElRef,
	listBottomAagElRef,
	_onScrollToUpper,
	_onScrollToLower,
	_scrollIntoView,
	_scrollIntoViewByNodeTop,
	_scrollToY,
	finalLowerThreshold,
	checkScrolledToBottom,
	handleVueScroll,
	updatePrivateScrollWithAnimation,
	pagingContentElRef,
	updateCachedSuperContentHeight,
	wxsPageScrollTop,
	scrollTopChange,
	pullDownDisTimeStamp,
	privateRefresherEnabled,
	refresherThresholdUnitConverted,
	finalRefresherEnabled,
	finalRefresherThresholdPlaceholder,
	finalRefresherThreshold,
	finalRefresherF2Threshold,
	finalRefresherFixedBacHeight,
	finalRefresherThemeStyle,
	finalRefresherOutRate,
	finalRefresherPullRate,
	finalRefresherTransform,
	finalRefresherTriggered,
	showRefresher,
	refresherTransform,
	refresherRevealStackCount,
	refresherTriggered,
	isTouchmoving,
	customRefresherHeight,
	showCustomRefresher,
	doRefreshAnimateAfter,
	isRefresherInComplete,
	_onRestore,
	loading,
	_refresherEnd,
	refresherTransition,
	handleScrollViewBounce,
	endRefresh,
	updateCustomRefresherHeight,
	handleRefresh,
	finalRefresherDefaultStyle,
	handleListTouchstart,
	handleRefresherTouchstart,
	handleTouchDirectionChange,
	isTouchEnded,
	// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
	touchDisabled,
	refresherTouchstart,
	getFinalRefresherMoveDis,
	_emitTouchmove,
	oldTouchDirection,
	oldEmitedTouchDirection,
	disabledBounce,
	_refresherTouchmove,
	_refresherTouchend,
	// #endif
	refreshEnd,
	handleRefresherTouchmove,
	handleRefresherTouchend,
	renderPropScrollTop,
	wxsOnPullingDown,
	handleWxsPullingDownStatusChange,
	handleWxsPullingDown,
	wxsPropType,
	handlePropUpdate,
	showF2,
	f2Transform,
	f2ElRef,
	nF2Opacity,
	handleGoF2,
	_handleCloseF2,
	closeF2,
	doRefresherRefreshAnimate,
	_doRefresherLoad,
	_updateCustomRefresherHeight,
	finalChatRecordMoreOffset,
	finalChatAdjustPositionOffset,
	chatRecordModeSafeAreaBottom,
	doChatRecordLoadMore,
	handleKeyboardHeightChange,
	handleScroll,
	refresherThresholdUpdateTag,
	nFixFreezing,
	setSpecialEffects,
	finalPagingStyle,
	handleToTop,
	isIosAndH5,
	// 虚拟列表
	virtualPlaceholderTopHeight,
	doInsertVirtualListItem,
	didUpdateVirtualListCell,
	didDeleteVirtualListCell,
	updateVirtualListRender,
	virtualPlaceholderBottomHeight
} = useHooks(option, emit);

// 当前z-paging内容的样式
const finalPagingContentStyle = computed(() => {
	let style: Record<string, any> = {
		...props.param.pagingContentStyle,
		transform: virtualPlaceholderTopHeight.value > 0 ? `translateY(${virtualPlaceholderTopHeight.value}px)` : 'none'
	};
	if (props.param.contentZIndex != 1) {
		style['z-index'] = props.param.contentZIndex;
		style['position'] = 'relative';
	}
	return style;
});

const bemClass = computed(() => {
	return [
		is('page', props.param?.usePageScroll),
		is('reached-top', renderPropScrollTop.value < 1),
		is('use-chat-record-mode', props.param?.useChatRecordMode),
		is('full', !props.param?.usePageScroll),
		is('fixed', !props.param?.usePageScroll && props.param?.fixed)
	];
});

// 是否是比较老的webview，在一些老的webview中，需要进行一些特殊处理
const isOldWebView = (() => {
	// #ifndef  MP-KUAISHOU
	// try {
	// 	const systemInfos = uni.getSystemInfoSync().system.split(' ');
	// 	const deviceType = systemInfos[0];
	// 	const version = parseInt(systemInfos[1]);
	// 	if ((deviceType === 'iOS' && version <= 10) || (deviceType === 'Android' && version <= 6)) {
	// 		return true;
	// 	}
	// 	return false;
	// } catch (e) {
	// 	return false;
	// }
	// #endif
	return false;
})();
// 当前是否是旧版webview
const finalIsOldWebView = computed(() => {
	return isOldWebView && !props.param?.usePageScroll;
});

const finalEnableBackToTop = computed(() => {
	return props.param?.usePageScroll ? false : props.param?.enableBackToTop;
});

const handleWxsTouch = (
	type: string,
	touch: {
		touchX: number;
		touchY: number;
	}
) => {
	if (type === 'start') {
		emit('touchstart', touch);
	} else if (type === 'move') {
		emit('touchmove', touch);
	} else {
		emit('touchend', touch);
	}
};

defineExpose({
	handleListTouchstart,
	handleRefresherTouchstart,
	handleTouchDirectionChange,
	handleScrollViewBounce,
	handleWxsPullingDown,
	handleRefresherTouchmove,
	handleRefresherTouchend,
	handlePropUpdate,
	handleWxsPullingDownStatusChange,
	handleToTop,
	endRefresh,
	handleWxsTouch,
	doInsertVirtualListItem,
	didUpdateVirtualListCell,
	didDeleteVirtualListCell,
	updateVirtualListRender
});
</script>

<style lang="scss" scoped>
@import '../../libs/theme/scroll-view.scss';
</style>
